using System;
using System.Collections.Generic;
using System.IO;
using CodePlex.TfsLibrary.ClientEngine;
using CodePlex.TfsLibrary.ObjectModel;
using CodePlex.TfsLibrary.RepositoryWebSvc;
using CodePlex.UnitTestUtility;
using Xunit;

namespace CodePlex.TfsLibrary
{
    public class PatchEngineTest
    {
        public class ApplyPatch
        {
            [Fact]
            public void AddFile_CallsTfsEngine()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <add path=""$/Foo/add.txt"" type=""file"" compression=""deflate"">7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8izqvq/wE=</add>
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                SourceItem actualItem = null;
                SourceItemResult actualResult = SourceItemResult.E_AccessDenied;

                engine.ApplyPatch(@"C:\Foo", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualItem = item;
                                                            actualResult = result;
                                                        });

                Assert.Equal("foo", engine.FileSystem.ReadAllText(@"C:\Foo\add.txt"));
                Assert.Equal(engine.TfsEngine.Add__CallbackData[0].SourceItem, actualItem);
                Assert.Equal(engine.TfsEngine.Add__CallbackData[0].SourceItemResult, actualResult);
                Assert.Equal(@"C:\Foo\add.txt", engine.TfsEngine.Add_LocalPath);
                Assert.False(engine.TfsEngine.Add_Recursive);
            }

            [Fact]
            public void AddFile_ExistingFile()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <add path=""$/Foo/add.txt"" type=""file"" compression=""deflate"">7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8izqvq/wE=</add>
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\add.txt", "here already");
                SourceItem actualItem = null;
                SourceItemResult actualResult = SourceItemResult.E_AccessDenied;

                engine.ApplyPatch(@"C:\Foo", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualItem = item;
                                                            actualResult = result;
                                                        });

                Assert.Equal(@"C:\Foo\add.txt", actualItem.LocalName);
                Assert.Equal(SourceItemResult.E_WontClobberLocalItem, actualResult);
            }

            [Fact]
            public void AddFolder_CallsTfsEngine()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <add path=""$/Foo/Add"" type=""folder"" />
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                SourceItem actualItem = null;
                SourceItemResult actualResult = SourceItemResult.E_AccessDenied;

                engine.ApplyPatch(@"C:\Foo", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualItem = item;
                                                            actualResult = result;
                                                        });

                Assert.True(engine.FileSystem.DirectoryExists(@"C:\Foo\Add"));
                Assert.Equal(engine.TfsEngine.Add__CallbackData[0].SourceItem, actualItem);
                Assert.Equal(engine.TfsEngine.Add__CallbackData[0].SourceItemResult, actualResult);
                Assert.Equal(@"C:\Foo\Add", engine.TfsEngine.Add_LocalPath);
                Assert.False(engine.TfsEngine.Add_Recursive);
            }

            [Fact]
            public void AddFolder_ExistingLocalFolder()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <add path=""$/Foo/Add"" type=""folder"" />
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                SourceItemResult actualResult = SourceItemResult.E_AccessDenied;

                engine.ApplyPatch(@"C:\Foo", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualResult = result;
                                                        });

                Assert.Equal(SourceItemResult.S_Ok, actualResult);
            }

            [Fact]
            public void BadXml()
            {
                string badXml = "Foo bar";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();

                Assert.Throws<IOException>(delegate
                                           {
                                               engine.ApplyPatch(@"C:\Foo", badXml, delegate {});
                                           });
            }

            [Fact]
            public void DeleteFile_CallsTfsEngine()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <delete path=""$/Foo/delete.txt"" csid=""8"" type=""file"" />
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                engine.FileSystem.WriteAllText(@"C:\Foo\delete.txt", "abc");
                SourceItem actualItem = null;
                SourceItemResult actualResult = SourceItemResult.E_AccessDenied;

                engine.ApplyPatch(@"C:\Foo", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualItem = item;
                                                            actualResult = result;
                                                        });

                Assert.Equal(@"C:\Foo\delete.txt", engine.TfsEngine.Delete_LocalPath);
                Assert.False(engine.TfsEngine.Delete_Force);
                Assert.Equal(engine.TfsEngine.Delete__CallbackData[0].SourceItem, actualItem);
                Assert.Equal(engine.TfsEngine.Delete__CallbackData[0].SourceItemResult, actualResult);
            }

            [Fact]
            public void DeleteFile_LocalModified()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <delete path=""$/Foo/delete.txt"" csid=""8"" type=""file"" />
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\delete.txt", "here i am");
                engine.TfsEngine.GetSourceItem__Result = SourceItem.FromLocalFile(4, SourceItemStatus.Modified, SourceItemStatus.Modified,
                                                                                  @"C:\Foo\delete.txt", null, 0, 0, null);
                SourceItem actualItem = null;
                SourceItemResult actualResult = SourceItemResult.S_Ok;

                engine.ApplyPatch(@"C:\Foo", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualItem = item;
                                                            actualResult = result;
                                                        });

                Assert.Equal(SourceItemResult.E_WontClobberLocalItem, actualResult);
                Assert.Equal(@"C:\Foo\delete.txt", actualItem.LocalName);
            }

            [Fact]
            public void DeleteFile_Unversioned()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <delete path=""$/Foo/delete.txt"" csid=""8"" type=""file"" />
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo");
                engine.FileSystem.WriteAllText(@"C:\Foo\delete.txt", "here i am");
                engine.TfsEngine.GetSourceItem__Result = SourceItem.FromLocalFile(4, SourceItemStatus.Unversioned, SourceItemStatus.Unversioned,
                                                                                  @"C:\Foo\delete.txt", null, 0, 0, null);
                SourceItem actualItem = null;
                SourceItemResult actualResult = SourceItemResult.S_Ok;

                engine.ApplyPatch(@"C:\Foo", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualItem = item;
                                                            actualResult = result;
                                                        });

                Assert.Equal(SourceItemResult.E_WontClobberLocalItem, actualResult);
                Assert.Equal(@"C:\Foo\delete.txt", actualItem.LocalName);
            }

            [Fact]
            public void DeleteFolder_CallsTfsEngine()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <delete path=""$/Foo/Delete"" csid=""14"" type=""folder"" />
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo\Delete");
                SourceItem actualItem = null;
                SourceItemResult actualResult = SourceItemResult.E_AccessDenied;

                engine.ApplyPatch(@"C:\Foo", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualItem = item;
                                                            actualResult = result;
                                                        });

                Assert.Equal(@"C:\Foo\Delete", engine.TfsEngine.Delete_LocalPath);
                Assert.False(engine.TfsEngine.Delete_Force);
                Assert.Equal(engine.TfsEngine.Delete__CallbackData[0].SourceItem, actualItem);
                Assert.Equal(engine.TfsEngine.Delete__CallbackData[0].SourceItemResult, actualResult);
            }

            [Fact]
            public void DeleteFolder_Unversioned()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <delete path=""$/Foo/Delete"" csid=""14"" type=""folder"" />
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                engine.FileSystem.EnsurePath(@"C:\Foo\Delete");
                engine.TfsEngine.GetSourceItem__Result = SourceItem.FromLocalPath(@"C:\Foo\Delete");
                SourceItem actualItem = null;
                SourceItemResult actualResult = SourceItemResult.E_AccessDenied;

                engine.ApplyPatch(@"C:\Foo", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualItem = item;
                                                            actualResult = result;
                                                        });

                Assert.Equal(@"C:\Foo\Delete", actualItem.LocalName);
                Assert.Equal(SourceItemResult.E_WontClobberLocalItem, actualResult);
            }

            [Fact]
            public void PatchFilesOutsideLocalDirectory()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <delete path=""$/Foo/delete.txt"" csid=""8"" type=""file"" />
  <delete path=""$/Foo/Delete"" csid=""14"" type=""folder"" />
  <update path=""$/Foo/modified.txt"" csid=""6"" compression=""deflate"">7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8i3syLJqX/tfM8XVSz4rzIZ+l5Uebpj54fPT96fvT86PnR86Pn//PP/wM=</update>
  <add path=""$/Foo/add.txt"" type=""file"" compression=""deflate"">7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8i3syLJqX/tfM8zWazfJaeF2We/uj50fOj50fPj54fPT96/j///D8=</add>
  <add path=""$/Foo/Add"" type=""folder"" />
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                engine.TfsEngine.GetFolderInfo_Result = new TfsFolderInfo("http://tfsUrl", "$/Bar");
                SourceItemResult actualResult = SourceItemResult.S_Ok;

                engine.ApplyPatch(@"C:\Bar", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualResult = result;
                                                        });

                Assert.Equal(SourceItemResult.E_PatchFilesOutsideLocalDirectory, actualResult);
            }

            [Fact]
            public void UpdateFile_LocalModified()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <update path=""$/Foo/modified.txt"" csid=""6"" compression=""deflate"">7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8i3syLJqX/tfM8XVSz4rzIZ+l5Uebpj54fPT96fvT86PnR86Pn//PP/wM=</update>
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                SourceItem actualItem = null;
                SourceItemResult actualResult = SourceItemResult.S_Ok;

                engine.ApplyPatch(@"C:\Foo", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualItem = item;
                                                            actualResult = result;
                                                        });

                Assert.Equal(SourceItemResult.E_WontClobberLocalItem, actualResult);
                Assert.Equal(TestablePatchEngineForApply.Item_ModifiedFile, actualItem);
            }

            [Fact]
            public void UpdateFile_LocalUnmodified()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <update path=""$/Foo/unmodified.txt"" csid=""5"" compression=""deflate"">7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8izqvq/wE=</update>
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                SourceItem actualItem = null;
                SourceItemResult actualResult = SourceItemResult.E_AccessDenied;

                engine.ApplyPatch(@"C:\Foo", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualItem = item;
                                                            actualResult = result;
                                                        });

                Assert.Equal(@"C:\Foo\unmodified.txt", engine.TfsEngine.Update_LocalPath);
                Assert.False(engine.TfsEngine.Update_Recursive);
                ChangesetVersionSpec v = Assert.IsType<ChangesetVersionSpec>(engine.TfsEngine.Update_Version);
                Assert.Equal(5, v.cs);
                Assert.Equal(SourceItemResult.S_Ok, actualResult);
                Assert.Equal(@"C:\Foo\unmodified.txt", actualItem.LocalName);
                Assert.Equal("foo", engine.FileSystem.ReadAllText(@"C:\Foo\unmodified.txt"));
            }

            [Fact]
            public void UpdateFile_WritesNewVersionsOfFiles()
            {
                string patchText = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <update path=""$/Foo/unmodified.txt"" csid=""5"">YWJj</update>
</patch>";
                TestablePatchEngineForApply engine = TestablePatchEngineForApply.Create();
                SourceItem actualItem = null;
                SourceItemResult actualResult = SourceItemResult.E_AccessDenied;

                engine.ApplyPatch(@"C:\Foo", patchText, delegate(SourceItem item,
                                                                 SourceItemResult result)
                                                        {
                                                            actualItem = item;
                                                            actualResult = result;
                                                        });

                Assert.Equal("abc", engine.FileSystem.ReadAllText(@"C:\Foo\unmodified.txt"));
                Assert.Equal(@"C:\Foo\unmodified.txt", actualItem.LocalName);
                Assert.Equal(SourceItemResult.S_Ok, actualResult);
            }

            class TestablePatchEngineForApply : PatchEngine
            {
                public static readonly SourceItem Item_ModifiedFile =
                    SourceItem.FromLocalItem(5, ItemType.File, SourceItemStatus.Modified, SourceItemStatus.Unmodified,
                                             @"C:\Foo\modified.txt", null, 6, 0, null);

                public static readonly SourceItem Item_Root =
                    SourceItem.FromLocalItem(1, ItemType.Folder, SourceItemStatus.Unmodified, SourceItemStatus.Unmodified,
                                             @"C:\Foo", null, 2, 0, null);

                public static readonly SourceItem Item_UnmodifiedFile =
                    SourceItem.FromLocalItem(3, ItemType.File, SourceItemStatus.Unmodified, SourceItemStatus.Unmodified,
                                             @"C:\Foo\unmodified.txt", null, 4, 0, null);

                public readonly MockFileSystem FileSystem;
                public readonly StubTfsEngine TfsEngine;

                public TestablePatchEngineForApply(StubTfsEngine engine,
                                                   MockFileSystem fileSystem)
                    : base(engine, fileSystem)
                {
                    TfsEngine = engine;
                    FileSystem = fileSystem;
                }

                public static TestablePatchEngineForApply Create()
                {
                    StubTfsEngine engine = new StubTfsEngine();
                    MockFileSystem fileSystem = new MockFileSystem();

                    fileSystem.EnsurePath(Item_Root.LocalName);

                    engine.Status__CallbackData.Clear();
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_Root, SourceItemResult.S_Ok));
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_UnmodifiedFile, SourceItemResult.S_Ok));
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_ModifiedFile, SourceItemResult.S_Ok));

                    engine.GetFolderInfo_Result = new TfsFolderInfo("http://tfsUrl", "$/Foo");

                    return new TestablePatchEngineForApply(engine, fileSystem);
                }
            }
        }

        public class CreatePatch
        {
            [Fact]
            public void BuilderCreatesXmlForCommitableItems()
            {
                string expected = @"<?xml version=""1.0""?>
<patch xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns=""http://www.codeplex.com/schema/PatchSchema-1.0.xsd"">
  <delete path=""$/Foo/delete.txt"" csid=""8"" type=""file"" />
  <delete path=""$/Foo/Delete"" csid=""14"" type=""folder"" />
  <update path=""$/Foo/modified.txt"" csid=""6"" compression=""deflate"">7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8i3syLJqX/tfM8XVSz4rzIZ+l5Uebpj54fPT96fvT86PnR86Pn//PP/wM=</update>
  <add path=""$/Foo/add.txt"" type=""file"" compression=""deflate"">7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8i3syLJqX/tfM8zWazfJaeF2We/uj50fOj50fPj54fPT96/j///D8=</add>
  <add path=""$/Foo/Add"" type=""folder"" />
</patch>";
                TestablePatchEngineForCreate engine = TestablePatchEngineForCreate.Create();

                string xml = engine.CreatePatch(@"C:\Foo", delegate {});

                Assert.Equal(expected, xml);
            }

            [Fact]
            public void CallsGetFolderInfo()
            {
                TestablePatchEngineForCreate engine = TestablePatchEngineForCreate.Create();

                engine.CreatePatch(@"C:\Foo", delegate {});

                Assert.Equal(@"C:\Foo", engine.TfsEngine.GetFolderInfo_Directory);
            }

            [Fact]
            public void CallsStatusForLocalRecursive()
            {
                TestablePatchEngineForCreate engine = TestablePatchEngineForCreate.Create();

                engine.CreatePatch(@"C:\Foo", delegate {});

                Assert.False(engine.TfsEngine.Status_IncludeServer);
                Assert.Equal(@"C:\Foo", engine.TfsEngine.Status_LocalPath);
                Assert.True(engine.TfsEngine.Status_Recursive);
            }

            [Fact]
            public void NothingToPatch()
            {
                TestablePatchEngineForCreate engine = TestablePatchEngineForCreate.Create();
                engine.TfsEngine.Status__CallbackData.Clear();

                string xml = engine.CreatePatch(@"C:\Foo", delegate
                                                           {
                                                               throw new InvalidOperationException("Should not get here");
                                                           });

                Assert.Null(xml);
            }

            [Fact]
            public void NotUnderSourceControl()
            {
                TestablePatchEngineForCreate engine = TestablePatchEngineForCreate.Create();
                engine.FileSystem.EnsurePath(@"C:\Bar");
                engine.TfsEngine.IsFolderTracked__Result = false;

                SourceItemResult actualResult = SourceItemResult.S_Ok;
                engine.CreatePatch(@"C:\Bar", delegate(SourceItem item,
                                                       SourceItemResult result)
                                              {
                                                  actualResult = result;
                                              });

                Assert.Equal(SourceItemResult.E_NotUnderSourceControl, actualResult);
            }

            [Fact]
            public void PathNotFound()
            {
                TestablePatchEngineForCreate engine = TestablePatchEngineForCreate.Create();

                SourceItemResult actualResult = SourceItemResult.S_Ok;
                engine.CreatePatch(@"C:\Bar", delegate(SourceItem item,
                                                       SourceItemResult result)
                                              {
                                                  actualResult = result;
                                              });

                Assert.Equal(SourceItemResult.E_DirectoryNotFound, actualResult);
            }

            [Fact]
            public void ShouldCallBackWithStatusForEachCommitableItem()
            {
                List<SourceItemCallbackData> data = new List<SourceItemCallbackData>();
                TestablePatchEngineForCreate engine = TestablePatchEngineForCreate.Create();

                engine.CreatePatch(@"C:\Foo", delegate(SourceItem item,
                                                       SourceItemResult result)
                                              {
                                                  data.Add(new SourceItemCallbackData(item, result));
                                              });

                Assert.Equal(TestablePatchEngineForCreate.CallbackItems.Length, data.Count);

                foreach (SourceItem item in TestablePatchEngineForCreate.CallbackItems)
                    Assert.NotNull(data.Find(delegate(SourceItemCallbackData cbData)
                                             {
                                                 return cbData.SourceItem.LocalName == item.LocalName;
                                             }));
            }

            class TestablePatchEngineForCreate : PatchEngine
            {
                public static readonly SourceItem[] CallbackItems;

                public static readonly SourceItem Item_AddedFile =
                    SourceItem.FromLocalItem(0, ItemType.File, SourceItemStatus.Add, SourceItemStatus.Add,
                                             @"C:\Foo\add.txt", null, 0, 0, null);

                public static readonly string Item_AddedFile_Contents = "This is the added file";

                public static readonly SourceItem Item_AddedFolder =
                    SourceItem.FromLocalItem(0, ItemType.Folder, SourceItemStatus.Add, SourceItemStatus.Add,
                                             @"C:\Foo\Add", null, 0, 0, null);

                public static readonly SourceItem Item_DeletedFile =
                    SourceItem.FromLocalItem(7, ItemType.File, SourceItemStatus.Delete, SourceItemStatus.Delete,
                                             @"C:\Foo\delete.txt", null, 8, 0, null);

                public static readonly SourceItem Item_DeletedFolder =
                    SourceItem.FromLocalItem(13, ItemType.Folder, SourceItemStatus.Delete, SourceItemStatus.Delete,
                                             @"C:\Foo\Delete", null, 14, 0, null);

                public static readonly SourceItem Item_MissingFile =
                    SourceItem.FromLocalItem(9, ItemType.File, SourceItemStatus.Missing, SourceItemStatus.Unmodified,
                                             @"C:\Foo\missing.txt", null, 10, 0, null);

                public static readonly SourceItem Item_ModifiedFile =
                    SourceItem.FromLocalItem(5, ItemType.File, SourceItemStatus.Modified, SourceItemStatus.Unmodified,
                                             @"C:\Foo\modified.txt", null, 6, 0, null);

                public static readonly string Item_ModifiedFile_Contents = "This is the modified file";

                public static readonly SourceItem Item_Root =
                    SourceItem.FromLocalItem(1, ItemType.Folder, SourceItemStatus.Unmodified, SourceItemStatus.Unmodified,
                                             @"C:\Foo", null, 2, 0, null);

                public static readonly SourceItem Item_UnmodifiedFile =
                    SourceItem.FromLocalItem(3, ItemType.File, SourceItemStatus.Unmodified, SourceItemStatus.Unmodified,
                                             @"C:\Foo\unmodified.txt", null, 4, 0, null);

                public static readonly SourceItem Item_UnmodifiedFolder =
                    SourceItem.FromLocalItem(11, ItemType.Folder, SourceItemStatus.Unmodified, SourceItemStatus.Unmodified,
                                             @"C:\Foo\Unmodified", null, 12, 0, null);

                public static readonly SourceItem Item_UnversionedFile =
                    SourceItem.FromLocalItem(0, ItemType.File, SourceItemStatus.Unversioned, SourceItemStatus.Unversioned,
                                             @"C:\Foo\unversioned.txt", null, 0, 0, null);

                public static readonly SourceItem Item_UnversionedFolder =
                    SourceItem.FromLocalItem(0, ItemType.Folder, SourceItemStatus.Unversioned, SourceItemStatus.Unversioned,
                                             @"C:\Foo\Unversioned", null, 0, 0, null);

                public readonly MockFileSystem FileSystem;
                public readonly StubTfsEngine TfsEngine;

                static TestablePatchEngineForCreate()
                {
                    CallbackItems = new SourceItem[]
                        {
                            Item_ModifiedFile,
                            Item_AddedFile,
                            Item_DeletedFile,
                            Item_UnversionedFile,
                            Item_MissingFile,
                            Item_AddedFolder,
                            Item_DeletedFolder,
                            Item_UnversionedFolder
                        };

                    string spaces = "                                        ";

                    for (int idx = 0; idx < 50; idx++)
                    {
                        Item_ModifiedFile_Contents += spaces;
                        Item_AddedFile_Contents += spaces;
                    }
                }

                public TestablePatchEngineForCreate(StubTfsEngine engine,
                                                    MockFileSystem fileSystem)
                    : base(engine, fileSystem)
                {
                    TfsEngine = engine;
                    FileSystem = fileSystem;
                }

                public static TestablePatchEngineForCreate Create()
                {
                    StubTfsEngine engine = new StubTfsEngine();
                    MockFileSystem fileSystem = new MockFileSystem();

                    fileSystem.EnsurePath(Item_Root.LocalName);
                    fileSystem.WriteAllText(Item_ModifiedFile.LocalName, Item_ModifiedFile_Contents);
                    fileSystem.WriteAllText(Item_AddedFile.LocalName, Item_AddedFile_Contents);

                    engine.Status__CallbackData.Clear();
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_Root, SourceItemResult.S_Ok));
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_UnmodifiedFile, SourceItemResult.S_Ok));
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_ModifiedFile, SourceItemResult.S_Ok));
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_AddedFile, SourceItemResult.S_Ok));
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_DeletedFile, SourceItemResult.S_Ok));
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_UnversionedFile, SourceItemResult.S_Ok));
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_MissingFile, SourceItemResult.S_Ok));
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_AddedFolder, SourceItemResult.S_Ok));
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_UnmodifiedFolder, SourceItemResult.S_Ok));
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_DeletedFolder, SourceItemResult.S_Ok));
                    engine.Status__CallbackData.Add(new SourceItemCallbackData(Item_UnversionedFolder, SourceItemResult.S_Ok));

                    engine.GetFolderInfo_Result = new TfsFolderInfo("http://tfsUrl", "$/Foo");

                    return new TestablePatchEngineForCreate(engine, fileSystem);
                }
            }
        }
    }
}